package com.hjy.http.download;

import android.os.Handler;
import android.util.Log;

import com.hjy.http.CustomHttpClient;
import com.hjy.http.cache.DownloadCacheHelper;
import com.hjy.http.cache.db.DBSql;
import com.hjy.http.download.listener.OnDownloadProgressListener;
import com.hjy.http.download.listener.OnDownloadingListener;
import com.hjy.http.upload.progressaware.ProgressAware;

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Map;

import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * Created by hjy on 8/5/15.<br>
 */
public class FileDownloadTask implements Runnable {

    private DownloadManager downloadManager;
    private FileDownloadInfo fileDownloadInfo;
    private OnDownloadingListener downloadingListener;
    private OnDownloadProgressListener progressListener;
    private volatile ProgressAware progressAware;

    private long currSize;
    private long totalSize;

    private long startTime;

    public final static int STATUS_DOWNLOADING = 0;
    public final static int STATUS_DOWNLOAD_SUCCESS = 1;
    public final static int STATUS_DOWNLOAD_FAILED = 2;

    private int downloadStatus = STATUS_DOWNLOADING;

    /**
     * 是否同步加载
     */
    private boolean isSyncLoading = false;


    //true为删除旧的重新下载， false为继续未完成的下载
    private boolean downloadNew = true;

    public FileDownloadTask(FileDownloadInfo fileDownloadInfo, DownloadManager downloadManager, ProgressAware progressAware) {
        this.fileDownloadInfo = fileDownloadInfo;
        downloadingListener = fileDownloadInfo.getOnDownloadingListener();
        progressListener = fileDownloadInfo.getOnDownloadProgressListener();
        this.downloadManager = downloadManager;
        this.progressAware = progressAware;
        this.startTime = System.currentTimeMillis();
    }

    public void setDownloadingListener(OnDownloadingListener downloadingListener) {
        this.downloadingListener = downloadingListener;
    }

    public void setProgressListener(OnDownloadProgressListener progressListener) {
        this.progressListener = progressListener;
    }

    public OnDownloadingListener getDownloadingListener() {
        return downloadingListener;
    }

    public OnDownloadProgressListener getProgressListener() {
        return progressListener;
    }

    public void setSyncLoading(boolean isSyncLoading) {
        this.isSyncLoading = isSyncLoading;
    }

    public boolean isSyncLoading() {
        return isSyncLoading;
    }

    public void resetProgressAware(final ProgressAware progressAware, Handler handler) {
        this.progressAware = progressAware;
        if(progressAware != null) {
            long t = totalSize;
            if(t == 0)
                t = Integer.MAX_VALUE;
            final int progress = (int)((currSize / (float) t) * 100);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    progressAware.setProgress(progress);
                }
            });
        }
    }

    public void setDownloadNew(boolean downloadNew) {
        this.downloadNew = downloadNew;
    }

    @Override
    public void run() {
        if (downloadNew) {
            DownloadCacheHelper.getInstance().delete(fileDownloadInfo.getUrl());
            DownloadCacheHelper.getInstance().insert(fileDownloadInfo.getUrl(),
                    fileDownloadInfo.getOutFile().getPath(), STATUS_DOWNLOADING + "",
                    getStartTime() + "", totalSize + "", "0", "");
        } else {
            Map<String, String> record = DownloadCacheHelper.getInstance().getRecord(fileDownloadInfo.getUrl());
            if (record == null) {
                DownloadCacheHelper.getInstance().insert(fileDownloadInfo.getUrl(),
                        fileDownloadInfo.getOutFile().getPath(), STATUS_DOWNLOADING + "",
                        getStartTime() + "", totalSize + "", "0", "");
            } else {
                String downloadedSize = record.get(DBSql.COL_DLHIS_FILE_DOWNLOAD_SIZE);
                currSize = Long.parseLong(downloadedSize);
            }
        }



        Request req = null;
        try {
            Request.Builder builder = new Request.Builder()
                    .url(fileDownloadInfo.getUrl())
                    .tag(generateTag(fileDownloadInfo))
                    .addHeader("Accept-Encoding", "identity");
            if (currSize > 0) {
                builder.addHeader("RANGE", "bytes=" + currSize + "-");
            }
            req = builder.build();
        } catch (Exception e) {
            e.printStackTrace();
            //url非法
            if(downloadingListener != null)
                downloadingListener.onDownloadFailed(this, DownloadErrorType.ERROR_URL_INVALID, e.getMessage());
            DownloadCacheHelper.getInstance().update(fileDownloadInfo.getUrl(),
                    fileDownloadInfo.getOutFile().getPath(), STATUS_DOWNLOAD_FAILED + "",
                    getStartTime() + "", totalSize + "", "0", "");
            return;
        }
        RandomAccessFile randomAccessFile = null;
        FileChannel channelOut = null;
        InputStream is = null;
        try {
            Response resp = CustomHttpClient.execute(req);
            if(resp.isSuccessful()) {
                ResponseBody body = resp.body();
                long contentLength = body.contentLength();
                if (totalSize == 0) {
                    totalSize = contentLength;
                }
                is = body.byteStream();
                randomAccessFile = new RandomAccessFile(fileDownloadInfo.getOutFile(), "rwd");
                randomAccessFile.seek(currSize);
                channelOut = randomAccessFile.getChannel();
                MappedByteBuffer mappedByteBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE, currSize, contentLength);

//                FileOutputStream fos = new FileOutputStream(fileDownloadInfo.getOutFile());
                byte[] buffer = new byte[1024];
                int size = 0;
                long currentSize = currSize;
                while ((size = is.read(buffer)) != -1) {
                    mappedByteBuffer.put(buffer,0, size);
                    currentSize += size;
                    int progress = (int) (100.0 * this.currSize / totalSize);
                    int newProgress = (int) (100.0 * currentSize / totalSize);
                    if (progress != newProgress) {
                        DownloadCacheHelper.getInstance().updateProgress(fileDownloadInfo.getUrl(), currentSize, totalSize);
                    }
                    this.currSize = currentSize;
                    downloadStatus = STATUS_DOWNLOADING;
                    if(progressListener != null) {
                        progressListener.onProgressUpdate(this, currentSize, totalSize);
                    }

                }
                downloadStatus = STATUS_DOWNLOAD_SUCCESS;
                if(downloadingListener != null)
                    downloadingListener.onDownloadSucc(this, fileDownloadInfo.getOutFile());
                DownloadCacheHelper.getInstance().update(fileDownloadInfo.getUrl(), fileDownloadInfo.getOutFile().getPath(), STATUS_DOWNLOAD_SUCCESS + "" , getStartTime() + "", totalSize + "", totalSize +"", "");
            } else {
                downloadStatus = STATUS_DOWNLOAD_FAILED;
                if(downloadingListener != null)
                    downloadingListener.onDownloadFailed(this, DownloadErrorType.ERROR_OTHER, resp.toString());
                DownloadCacheHelper.getInstance().update(fileDownloadInfo.getUrl(), fileDownloadInfo.getOutFile().getPath(), STATUS_DOWNLOAD_FAILED + "", getStartTime() + "", totalSize + "", currSize + "", "");
            }
        } catch (IOException e) {
            e.printStackTrace();
            downloadStatus = STATUS_DOWNLOAD_FAILED;
            if(downloadingListener != null)
                downloadingListener.onDownloadFailed(this, DownloadErrorType.ERROR_NETWORK, e.getMessage());
            DownloadCacheHelper.getInstance().update(fileDownloadInfo.getUrl(), fileDownloadInfo.getOutFile().getPath(), STATUS_DOWNLOAD_FAILED + "", getStartTime() + "", totalSize + "", currSize + "", "");
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (channelOut != null) {
                    channelOut.close();
                }
                if (randomAccessFile != null) {
                    randomAccessFile.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        ProgressAware pa = progressAware;
        if(pa != null) {
            downloadManager.cancelUpdateProgressTaskFor(pa);
        }
    }

    private String generateTag(FileDownloadInfo fileDownloadInfo) {
        return fileDownloadInfo.getId() + fileDownloadInfo.getUrl().hashCode();
    }

    public FileDownloadInfo getFileDownloadInfo() {
        return fileDownloadInfo;
    }

    private boolean isProgressViewCollected(ProgressAware pa) {
        if(pa.isCollected())
            return true;
        return false;
    }

    private boolean isProgressViewReused(ProgressAware pa) {
        String downloadTaskId = downloadManager.getFileDownloadInfoIdForProgressAware(pa);
        if(!fileDownloadInfo.getId().equals(downloadTaskId))
            return true;
        return false;
    }

    public void updateProgress(int progress) {
        ProgressAware pa = progressAware;
        if(pa != null) {
            if(!isProgressViewCollected(pa) && !isProgressViewReused(pa)) {
                pa.setProgress(progress);
            }
        }
    }

    public long getStartTime() {
        return startTime;
    }

    public int getProgress() {
        if (totalSize == 0) {
            return 0;
        }
        return (int) (100.0 * currSize / totalSize);
    }

    public int getDownloadStatus() {
        return downloadStatus;
    }

    //以下方法为恢复数据时使用
    public void setDownloadStatus(int downloadStatus) {
        this.downloadStatus = downloadStatus;
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

    public void setCurrSize(long currSize) {
        this.currSize = currSize;
    }

    public void setTotalSize(long totalSize) {
        this.totalSize = totalSize;
    }

}